#ifndef UTILS_H
#define UTILS_H

#include "config.h"
#include <stdio.h>
#include <signal.h>
#include <unistd.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>
#include <errno.h>
#include <assert.h>
#include <fcntl.h>
#include <sys/epoll.h>
#include <sys/socket.h>
#include <math.h>
#include <list>
#include <queue>
using namespace std;

//与主函数通信的管道
int sig_pipe[2];

//信号处理函数，将信号通过管道发送给主函数
void sig_handler(int sig) {
	//为保证函数的可重入性，保留原来的errno
    int save_errno = errno;
    int msg = sig;
    int ret = send(sig_pipe[1], (char *)&msg, 1, 0);
    errno = save_errno;
}

//设置进程的信号处理函数
void addsig(int sig, void(handler)(int))
{
    struct sigaction sa;
    memset(&sa, '\0', sizeof(sa));
    sa.sa_handler = handler;
	sa.sa_flags |= SA_RESTART;
    sigfillset(&sa.sa_mask);
    assert(sigaction(sig, &sa, NULL) != -1);
}

//将文件描述符设置为非阻塞
int setnonblocking(int fd) {
    int old_option = fcntl(fd, F_GETFL);
    int new_option = old_option | O_NONBLOCK;
    fcntl(fd, F_SETFL, new_option);
    return old_option;
}

//向内核事件表注册读事件，LT模式
void addfd(int epollfd, int fd) {
	epoll_event event;
    event.data.fd = fd;
    event.events = EPOLLIN | EPOLLRDHUP;
    epoll_ctl(epollfd, EPOLL_CTL_ADD, fd, &event);
    setnonblocking(fd);
}

//将内核事件表中事件删除
void removefd(int epollfd, int fd) {
	epoll_ctl(epollfd, EPOLL_CTL_DEL, fd, 0);
    close(fd);
}

struct timerNode{
	time_t tm;  //接收到数据的时间
	double a, b, ab;
	timerNode() {}
	timerNode(time_t _tm, double _a, double _b, double _ab): 
		tm(_tm), a(_a), b(_b), ab(_ab) {}
};

//按时间升序存储window秒内收到的数据
class timerList {
public:
	timerList() {}
	timerList(const Config& conf):window(conf.window) {}
	~timerList() {}

	//删除列表中不在接收时间窗口[ddl-window, ddl]内的元素
	void ticker(time_t ddl);
	//在列表末尾添加一个元素
	void addNode(const timerNode& tnd);
	//计算相关统计结果并打印
	void calPrint();

public:
	time_t window;

private:
	list<timerNode> tlst;

private:
	//a，b，a*b的最大值，最小值，平均值，标准差，中位数
	double aMin, aMax, aAvg, aSdv, aMid;
	double bMin, bMax, bAvg, bSdv, bMid;
	double abMin, abMax, abAvg, abSdv, abMid;
	//(a, b)的协方差，相关系数
	double abCov, abCor;
	//list的长度
	int ltSize;
	//用来求中位数的堆
	priority_queue<double, vector<double>> aHeap, bHeap, abHeap;
};

void timerList::ticker(time_t ddl) {
	while (!tlst.empty() && tlst.front().tm < ddl - window) 
		tlst.pop_front();
}

void timerList::addNode(const timerNode& tnd) {
	tlst.push_back(tnd);
	ticker(tnd.tm);
}

void timerList::calPrint() {
	ltSize = tlst.size();
	if (ltSize == 0) return;

	double aSum = 0.0, bSum = 0.0, abSum = 0.0;
	double na, nb, nab;
	aMin = bMin = abMin = __DBL_MAX__;
	aMax = bMax = abMax = __DBL_MIN__;
	list<timerNode>::iterator it = tlst.begin();

	for (; it != tlst.end(); ++it) {
		na = it->a; nb = it->b; nab = it->ab;
		aSum += na; bSum += nb; abSum += nab;
		aHeap.push(na); bHeap.push(nb); abHeap.push(nab);
		if (na < aMin) aMin = na;
		else if (na > aMax) aMax = na;
		if (nb < bMin) bMin = nb;
		else if (nb > bMax) bMax = nb;
		if (nab < abMin) abMin = nab;
		else if (nab > abMax) abMax = nab;
	}

	aAvg = aSum / ltSize;
	bAvg = bSum / ltSize;
	abAvg = abSum / ltSize;

	aSum = bSum = abSum = 0.0;
	double covSum = 0.0;
	int midSize = ltSize / 2;
	bool tag = (ltSize % 2) == 0;
	it = tlst.begin();

	for (int i = 0; i < ltSize; ++i) {
		if (i == midSize - 1 && tag) {
			aMid = aHeap.top();
			bMid = bHeap.top();
			abMid = abHeap.top();
		} else if (i == midSize) {
			if (tag) {
				aMid = (aMid + aHeap.top()) / 2;
				bMid = (bMid + bHeap.top()) / 2;
				abMid = (abMid + abHeap.top()) / 2;
			} else {
				aMid = aHeap.top();
				bMid = bHeap.top();
				abMid = abHeap.top();
			}
		}
		aHeap.pop(); bHeap.pop(); abHeap.pop();

		na = it->a; nb = it->b; nab = it->ab;
		aSum += (na - aAvg) * (na - aAvg);
		bSum += (nb - bAvg) * (nb - bAvg);
		abSum += (nab - abAvg) * (nab - abAvg);
		covSum += (na - aAvg) * (nb - bAvg);

		++it;
	}

	aSdv = sqrt(aSum / ltSize);
	bSdv = sqrt(bSum / ltSize);
	abSdv = sqrt(abSum / ltSize);

	abCov = covSum / ltSize;
	abCor = abCov / (aSdv * bSdv);

	printf(
		"%.3f\t%.3f\t%.3f\t%.3f\t%.3f\t"
		"%.3f\t%.3f\t%.3f\t%.3f\t%.3f\t"
		"%.3f\t%.3f\t%.3f\t%.3f\t%.3f\t"
		"%.3f\t%.3f\t%d\n",
		aMin, aMax, aAvg, aSdv, aMid,
		bMin, bMax, bAvg, bSdv, bMid,
		abMin, abMax, abAvg, abSdv, abMid,
		abCov, abCor,ltSize
	);
}

#endif